home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / PopupWindow / CPopupWindowControl.cpp next >
Encoding:
Text File  |  1997-01-02  |  31.6 KB  |  1,215 lines  |  [TEXT/CWIE]

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //    CPopupWindowControl.h
  4. //
  5. //        The PopupWindow control is designed to bring up a window whenever the 
  6. //        mouse moves over the label designated as its source. Inside the window, 
  7. //        PopupWindow renders the contents of the specified URL
  8. //
  9. //        In this demonstration version, a mouse click in the specified label is 
  10. //        required, pending implementatin of a mouseOver event.
  11. //
  12. //        In this demonstration version, only pict files are rendered, pending 
  13. //        implementation of MSHTML to render the contents of URL's.
  14. //
  15. //        In this demonstration version, the image is rendered in a rect within 
  16. //        the browser window rather than in a separate window.
  17. //
  18.  
  19. #include "ocheaders.h"
  20. #include "CBaseControl.h"
  21. #include "CErrorControl.h"                //    included in header, listed here for documentation
  22. #include "CBaseBindStatusCallback.h"    //    included in header, listed here for documentation
  23. #include "CPopupWindowControl.h"
  24. #include "BDMacros.h"
  25. #include "BDUtils.h"
  26. #include "FnAssert.h"
  27. #include "CCPContainer.h"
  28. #include "CPopupWindowError.h"
  29. #include <stdio.h>
  30.  
  31. ///////////////////////////////////////////////////////////////////////////////
  32. //
  33. //  CPopupWindowControl::CPopupWindowControl
  34. //
  35. //        constructor
  36. //
  37.  
  38. CPopupWindowControl::CPopupWindowControl(void) : CBaseControl(), CBaseBindStatusCallback()
  39. {
  40.     CCPContainer*    containerObj = NULL;
  41.     long            i;    //    used as index
  42. #ifdef _DEBUG
  43.     HRESULT            returnValue;
  44.     IUnknown*        unk;
  45. #endif
  46.     
  47.     mFatalError = false;    //    To block action after failure.
  48.     
  49.     mContainerP = NULL;    //    These are CBaseControl instance variables and should 
  50.     mID[0] = 0;        //    be initialized in that constructor; but they aren't, 
  51.                         //    and they get used before they're set.
  52.     
  53.     mCookie = 0;    //    NOTE: if we ever actually use this for something, we need a 
  54.                     //    list of them, since we allow connections to more than
  55.                     //    one specific sources.
  56.     
  57.     mSourceName = NULL;
  58.     
  59.     mConnectingComplete = false;
  60.  
  61.     for ( i = 0; i < cMaxNumSourceControls; i++ )    //    Reset the source names array.
  62.     {
  63.         mDesiredSourceNames[i] = NULL;
  64.     }
  65.     mNumDesiredSources = -1;
  66.     
  67.     mNumFoundSources = 0;
  68.     mIsIdling = false;
  69.     
  70.     mPict = NULL;
  71.     mPictIsLoaded = false;
  72.     
  73.     mPopupWidth = 288;    //    Arbitrary for now. The Windows version sets it to the smaller 
  74.     mPopupHeight = 288;    //    of the browser width or 1/3 of screen size
  75.     
  76.     try
  77.     {
  78.         //    Set a default name.
  79.         
  80.         if ( !(SetName ( cPopupControlName )) ) 
  81.             throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  82.         
  83.         if ( !(SetSourceName ( cEmptyCString )) ) 
  84.             throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  85.         
  86. #ifdef _DEBUG
  87.         //    Since we're in debug code, don't add error handling here.
  88.         
  89.         returnValue = QueryInterface ( IID_IUnknown, (void**) &unk );
  90.         GetObjectName ( unk, mThisName );
  91. #endif
  92.  
  93.         //    Create the new connection point container object.
  94.         
  95.         containerObj = new CCPContainer ( cNumConnections );
  96.         if ( !containerObj ) throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  97.         
  98.         //    Snag the interface pointer.
  99.         
  100.         if ( FAILED ( containerObj->QueryInterface ( IID_IConnectionPointContainer, 
  101.                                                      &mCPContainerP ) ) )
  102.             throw CPopupWindowError ( NO_CNXN_PT_CONTAINER_ERROR );
  103.         
  104.         if ( mCPContainerP )
  105.         {
  106.             //    Allocate the connection points. We support 1 connection point.
  107.             
  108.             if ( FAILED ( containerObj->AddConnectionPoint ( IID_IDidMenuEvents ) ) )
  109.                 throw CPopupWindowError ( CANT_ADD_CNXN_PT_ERROR );
  110.         }
  111.     }
  112.     
  113.     //    error handling
  114.     
  115.     catch ( CPopupWindowError &popupWindowError )
  116.     {
  117.         //    Any failure here is catastrophic...
  118.         
  119.         mFatalError = true;        
  120.         popupWindowError.HandleError ();
  121.     }
  122. }
  123.  
  124. ///////////////////////////////////////////////////////////////////////////////
  125. //
  126. //  CPopupWindowControl::~CPopupWindowControl
  127. //
  128.  
  129. CPopupWindowControl::~CPopupWindowControl(void)
  130. {
  131.     long            i;    //    used as an index
  132.     
  133.     if ( mIsIdling && mContainerSiteP )
  134.     {
  135.         //    Remove our idler.
  136.         
  137.         mContainerSiteP->SetIdleTime ( RemoveAllIdlers, 0 );
  138.     }
  139.     
  140.     mID[0] = 0;
  141.     
  142.     if ( mSourceName )
  143.     {
  144.         delete [] mSourceName;
  145.         mSourceName = NULL;
  146.     }
  147.     
  148.     if ( mNumDesiredSources > 0 )
  149.     {
  150.         //    Get rid of the strings in our source name list.
  151.         
  152.         for ( i = 0; i < mNumDesiredSources; i++ )
  153.         {
  154.             ASSERT ( mDesiredSourceNames[i] != NULL, "Unexpected NULL source name.");
  155.             delete [] mDesiredSourceNames[i];
  156.             mDesiredSourceNames[i] = NULL;
  157.         }
  158.         mNumDesiredSources = -1;
  159.     }
  160.     
  161.     if (mPict)
  162.     {
  163.         //    Dispose the Picture, then the handle itself.
  164.         
  165.         ::DisposeHandle ( (Handle)((*mPict)->Pic) );
  166.         ::DisposeHandle ( (Handle)mPict );
  167.         mPict = NULL;
  168.     }
  169. }
  170.  
  171. ///////////////////////////////////////////////////////////////////////////////
  172. //
  173. //  CPopupWindowControl::CBaseControl::IObjectWithSite::SetSite
  174. //
  175. STDMETHODIMP
  176. CPopupWindowControl::SetSite ( IUnknown* inClientSite )
  177. {
  178.     HRESULT returnValue;
  179.     
  180.     returnValue = CBaseControl::SetSite ( inClientSite );
  181.     
  182.     if ( mContainerSiteP )
  183.     {
  184.         mIsIdling = SUCCEEDED ( mContainerSiteP->SetIdleTime ( 0, 0 ) );
  185.     }
  186.     else
  187.     {
  188.         mIsIdling = false;
  189.     }
  190.     
  191.     return returnValue;
  192. }
  193.  
  194. ///////////////////////////////////////////////////////////////////////////////
  195. //
  196. //  CPopupWindowControl::IControl::Draw
  197. //
  198.  
  199. STDMETHODIMP
  200. CPopupWindowControl::Draw ( DrawContext* Context )
  201. {
  202.     HRESULT            returnValue = ResultFromScode ( S_OK );
  203.     
  204.     if ( Context->DrawAspect != DVASPECT_CONTENT )
  205.     {
  206.         returnValue = ResultFromScode ( DV_E_DVASPECT );
  207.     }
  208.  
  209.     return returnValue;
  210. }
  211.  
  212. ///////////////////////////////////////////////////////////////////////////////
  213. //
  214. //  CPopupWindowControl::CBaseControl::IControl::DoIdle
  215. //
  216.  
  217. STDMETHODIMP
  218. CPopupWindowControl::DoIdle ( Uint32 IdleRefCon )
  219. {
  220.     Boolean                        alreadyAdded;
  221.     IConnectionPointContainer*    cnxnPtContainer = NULL;
  222.     IConnectionPoint*            cnxnPt = NULL;
  223.     char                        controlName[256];
  224.     IEnumUnknown*                enumUnknown;
  225.     long                        i;    //    used as an index
  226.     Boolean                        setAdviseSink;
  227.     IUnknown*                     testControl;
  228.     
  229.     // if the client site has been set and we haven't yet tried to connect,
  230.     // enumerate the other controls in this container to find the one we want
  231.     // to connect to.  We use the mConnectingComplete, mNumDesiredSources, and
  232.     // mNumFoundSources to handle the case where some of the controls we're 
  233.     // attempting to connect to aren't enumerated yet because they haven't
  234.     // loaded yet.  Once we find all the ones we're looking for, we stop 
  235.     // looking.
  236.     //
  237.     // Note that we used to check to advisory cookie (mCookie) instead of
  238.     // mConnectingComplete, but in our case now, we want to be able to connect
  239.     // to multiple specific sources, so we need a separate flag.
  240.  
  241. #pragma unused ( IdleRefCon )
  242.     
  243.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  244.  
  245.     // if we don't have a container yet
  246.     // Get it now
  247.     if ( !mContainerP )
  248.     {
  249.         mContainerSiteP->GetContainer(&mContainerP);
  250.         mContainerP->AddRef();
  251.     }
  252.     
  253.     if ( mContainerP && !mConnectingComplete )
  254.     {
  255.         try
  256.         {
  257.             //    Enumerate the objects.
  258.             
  259.             if ( FAILED ( mContainerP->EnumControls ( NULL, 
  260.                                                       OLECONTF_EMBEDDINGS, 
  261.                                                       &enumUnknown ) ) ) 
  262.                 throw CPopupWindowError ( CANT_ENUM_OBJECTS_ERROR, this );
  263.             
  264.             while ( enumUnknown->Next ( 1, &testControl, NULL ) == NOERROR )
  265.             {
  266.                 //    Get the name.  Do it here instead of in a more nested scope so that
  267.                 //    we can use if for debugging.
  268.                 
  269.                 GetObjectName ( testControl, controlName );
  270.  
  271.                 //    Try to get a connection point container from the control.
  272.                 
  273.                 testControl->QueryInterface ( IID_IConnectionPointContainer, 
  274.                                               (void**) &cnxnPtContainer );
  275.                 
  276.                 if ( cnxnPtContainer )
  277.                 {
  278.                     //    It has one, so this may be it. See if this connection point container 
  279.                     //    has a connection point with the outgoing interface we want.
  280.                     
  281.                     cnxnPtContainer->FindConnectionPoint ( IID_IDoMenuEvents, &cnxnPt );
  282.                 
  283.                     if ( cnxnPt )
  284.                     {
  285.                         //    We got it, so set an advise connection on the outgoing interface.
  286.                         
  287.                         setAdviseSink = false;
  288.                         
  289.                         if ( mNumDesiredSources < 0 )    //    We're not being specific.
  290.                         {
  291.                             //    We don't really care about this if mNumDesiredSources < 0, 
  292.                             //    but we'll do it anyway for the sake of completeness.
  293.                             
  294.                             if ( !(AddSource ( controlName, &alreadyAdded )) )
  295.                                 throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  296.                                 
  297.                             setAdviseSink = true;
  298.                         }
  299.                         else
  300.                         {
  301.                             for ( i = 0; i < mNumDesiredSources; i++ )
  302.                             {
  303.                                 ASSERT ( mDesiredSourceNames[i] != NULL, 
  304.                                          "Unexpected NULL source name!" );
  305.                                 if ( STRINGS_ARE_EQUAL ( mDesiredSourceNames[i], controlName ) )
  306.                                 {
  307.                                     //    It's one of our targets.
  308.                                     
  309.                                     if ( !(AddSource ( controlName, &alreadyAdded )) )
  310.                                         throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  311.                                     
  312.                                     if ( !alreadyAdded )
  313.                                     {
  314.                                         //    We don't want to set the advise sink if we've 
  315.                                         //    found it in the past.
  316.                                         
  317.                                         setAdviseSink = true;
  318.                                     }
  319.                                     
  320.                                     //    There's no need to loop any further once we've matched.
  321.                                     
  322.                                     break;
  323.                                 }
  324.                             }
  325.                         }
  326.                         
  327.                         if ( setAdviseSink )
  328.                         {
  329.                             if ( FAILED ( cnxnPt->Advise ( (IControl*)this, &mCookie ) ) )
  330.                             {
  331.                                 throw CPopupWindowError ( CANT_ADVISE_CNXN_ERROR );
  332.                             }
  333.                         }
  334.                     }
  335.                     
  336.                     cnxnPtContainer->Release();
  337.                 }
  338.             }
  339.             
  340.             //    Set a reminder to indicate whether we've already done our connecting.
  341.             
  342.             mConnectingComplete = ( mNumDesiredSources < 0 ) ? 
  343.                 true : ( mNumDesiredSources == mNumFoundSources );
  344.             
  345.             //    Don't forget to release.
  346.             
  347.             enumUnknown->Release();
  348.         }
  349.         
  350.         catch ( CPopupWindowError &popupWindowError )
  351.         {
  352.             //    Any failure here is fatal, because if we can't connect we can't pop up.
  353.             
  354.             mFatalError = true;        
  355.             popupWindowError.HandleError ();
  356.         }
  357.     }
  358.  
  359.     if ( mConnectingComplete )
  360.     {
  361.         //    We're hooked up, so discontinue idling.
  362.         
  363.         mContainerSiteP->SetIdleTime ( RemoveAllIdlers, 0 );
  364.         mIsIdling = false;
  365.     }
  366.     
  367.     if ( mPendingDraw )
  368.     {
  369.         //    We have a draw pending, so force a draw.
  370.         
  371.         InvalAllContexts ();
  372.     }
  373.  
  374.     return ResultFromScode ( S_OK );
  375. }
  376.  
  377. ///////////////////////////////////////////////////////////////////////////////
  378. //
  379. //  CPopupWindowControl::OnStopBinding
  380. //
  381.  
  382. STDMETHODIMP
  383. CPopupWindowControl::OnStopBinding(ErrorCode Result, const Char8* Error)
  384. {
  385.     Rect            boundsRect;
  386.     HRESULT            returnValue;
  387.     
  388.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  389.  
  390.     returnValue = CBaseBindStatusCallback::OnStopBinding ( Result, Error );    //    shouldn't fail
  391.  
  392.     if (mBindSiteP)
  393.     {
  394.         mBindSiteP->Release();    //    shouldn't fail
  395.         mBindSiteP = 0;
  396.     }
  397.             
  398.     //    That should be end of file. Prepare the pict to be drawn.
  399.     
  400.     mPictIsLoaded = true;
  401.     
  402.     try
  403.     {
  404.         if ( ( mPict == NULL ) || ( (*mPict)->Pic == NULL ) )
  405.             throw CPopupWindowError ( EOF_AND_NO_IMAGE_ERROR );
  406.     
  407.         boundsRect = (*(*mPict)->Pic)->picFrame;
  408.         ::OffsetRect( &boundsRect, -boundsRect.left, -boundsRect.top );
  409.         (*mPict)->PicRect = boundsRect;
  410.         
  411.         returnValue = ResultFromScode ( S_OK );
  412.     }
  413.     
  414.     catch ( CPopupWindowError &popupWindowError )
  415.     {
  416.         //    Any failure here is fatal, because if we don't have an image after 
  417.         //    the data has all been delivered, there's nothing to pop up.
  418.         
  419.         mFatalError = true;        
  420.         popupWindowError.HandleError ();
  421.         
  422.         returnValue = ResultFromScode ( E_FAIL );
  423.     }
  424.     
  425.     return returnValue;
  426. }
  427.  
  428. ///////////////////////////////////////////////////////////////////////////////
  429. //
  430. //  CPopupWindowControl::OnDataAvailable
  431. //
  432. //    OnDataAvailable is a callback routine which gets called when there is data 
  433. //    available in the input stream. The amount of data is specified. 
  434. //    OnDataAvailable grabs "Size" bytes from the "StgMedium" (storage medium) 
  435. //    and places it into the target buffer mPict.
  436. //    
  437. //    OnDataAvailable will be called repeatedly until there is no more data 
  438. //    available in the input stream. Note that OnStopBinding gets called at EOF.
  439. //    
  440. //    This method increases the size of the inbut buffer handle "mPict" by "Size" 
  441. //    bytes each time it's called.
  442. //
  443.  
  444. STDMETHODIMP
  445. CPopupWindowControl::OnDataAvailable(
  446.     Uint32            BSCF, 
  447.     Uint32            Size, 
  448.     FORMATETC*        FormatEtc, 
  449.     STGMEDIUM*        StgMedium)
  450. {
  451.     unsigned long    newSize;
  452.     HRESULT            returnValue = ResultFromScode ( S_OK );
  453.     Handle            TargetPicHandle = NULL;
  454.     
  455.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  456.     
  457.     //    CBaseBindStatusCallback::OnDataAvailable sets "mSize" based on "Size". (This should 
  458.     //    never fail.)
  459.     
  460.     returnValue = CBaseBindStatusCallback::OnDataAvailable ( BSCF, Size, FormatEtc, StgMedium );
  461.     
  462.     if ( StgMedium->tymed == TYMED_FILE ) 
  463.     {
  464.         CoTaskMemFree ( StgMedium->lpszFileName );
  465.         if ( StgMedium->pUnkForRelease != NULL )
  466.         {
  467.             StgMedium->pUnkForRelease->Release ();    //    Should not fail.
  468.         }
  469.     }
  470.     else if ( StgMedium->tymed == TYMED_ISTREAM )    //    Stream I/O
  471.     {
  472.         try
  473.         {
  474.             //    Make sure that the picture record and its buffer are allocated and have 
  475.             //    enough space to read the available data.
  476.                 
  477.             if ( !(AllocatePictBuffer ()) ) throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  478.             
  479.             mPictBytesRemaining = mDataSize;
  480.             if ( mPictBytesSoFar < cPictHeaderSize )
  481.             {
  482.                 //    Read the pict header. (Just read it into the PicHandle, and we'll 
  483.                 //    overwrite it when we get the real picture data.)
  484.                 
  485.                 if ( !(ReadPictHeader ( StgMedium )) )
  486.                     throw CPopupWindowError ( IMAGE_READ_ERROR );
  487.                 
  488.                 if ( mPictBytesSoFar >= cPictHeaderSize )
  489.                 {
  490.                     //    We're going to throw away the header, so reset the buffer 
  491.                     //    handle size.
  492.                     
  493.                     TargetPicHandle = (Handle)((*mPict)->Pic);
  494.                     newSize = ::GetHandleSize ( TargetPicHandle ) - cPictHeaderSize;
  495.                     ::SetHandleSize ( TargetPicHandle, newSize );
  496.                     
  497.                     //    Setting a handle smaller shouldn't fail...
  498.                     
  499.                     if ( ::GetHandleSize ( TargetPicHandle ) != newSize )
  500.                         throw CPopupWindowError ( DATA_REALLOCATION_ERROR );
  501.                 }
  502.             }
  503.             
  504.             //    It's possible we didn't read the entire picture header the first time, so 
  505.             //    we can't assume it's okay to go ahead and start reading data.
  506.             
  507.             if ( ( mPictBytesSoFar >= cPictHeaderSize ) && ( mDataSize > 0 ) )
  508.             {
  509.                 //    Read the available data into the picture buffer.
  510.             
  511.                 if ( !(ReadPictData ( StgMedium )) ) 
  512.                     throw CPopupWindowError ( IMAGE_READ_ERROR );
  513.             }
  514.         }
  515.         
  516.         catch ( CPopupWindowError &popupWindowError )
  517.         {
  518.             //    Any failure here is fatal, because we don't have an image to pop up.
  519.             
  520.             mFatalError = true;        
  521.             popupWindowError.HandleError ();
  522.             
  523.             returnValue = ResultFromScode ( E_FAIL );
  524.         }
  525.     }
  526.  
  527.     return returnValue;
  528. }
  529.  
  530. ///////////////////////////////////////////////////////////////////////////////
  531. //
  532. //  CPopupWindowControl::IDoMenuEvents::Popup
  533. //
  534.  
  535. STDMETHODIMP
  536. CPopupWindowControl::Popup ( IUnknown* Source, PlatformEvent* Event )
  537. {
  538. #pragma unused ( Source )
  539.  
  540.     DrawContext        Context;
  541.     GDHandle        currentGraphicsDevice;
  542.     GrafPtr            currentGrafPort;
  543.     pictInfo        CurrentPict;
  544.     EventRecord        dummyEvent;
  545.     StringPtr        ErrorMessage = NULL;
  546.     short            innermost;
  547.     Point            mouseCoord;
  548.     Rect            myRect;
  549.     RgnHandle        oldClipRgn;
  550.     Point            screenBotRight;
  551.     Rect            screenRect;
  552.     Point            screenTopLeft;
  553.     
  554.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  555.  
  556.     //    Get the target rect.
  557.  
  558.     mouseCoord = Event->where;
  559.     ::GlobalToLocal ( &mouseCoord );
  560.     
  561.     SetRect ( &myRect, mouseCoord.h, 
  562.                        mouseCoord.v, 
  563.                        mouseCoord.h + mPopupWidth - 1, 
  564.                        mouseCoord.v + mPopupHeight - 1);
  565.     
  566.     ::GetPort ( ¤tGrafPort );
  567.     
  568.     //    Get the current screen information.
  569.     
  570.     currentGraphicsDevice = ::GetGDevice ();
  571.     screenRect = (**currentGraphicsDevice).gdRect;
  572.     ::SetPt ( &screenTopLeft, screenRect.left, screenRect.top + GetMBarHeight () );
  573.     ::SetPt ( &screenBotRight, screenRect.right, screenRect.bottom );
  574.     ::GlobalToLocal ( &screenTopLeft );
  575.     ::GlobalToLocal ( &screenBotRight );
  576.     ::Pt2Rect (  screenTopLeft, screenBotRight, &screenRect );
  577.     
  578.     //mPopupWidth = (screenRect.right - screenRect.left)/3;    //    1/3 of screen size
  579.     //mPopupHeight = (screenRect.bottom - screenRect.top)/3;    //    1/3 of screen size
  580.     
  581.     //    Force myRect onscreen and into the window.
  582.     
  583.     innermost = MIN ( currentGrafPort->portRect.bottom, screenRect.bottom );
  584.     if ( myRect.bottom > innermost )
  585.     {
  586.         OffsetRect ( &myRect, 0, innermost - myRect.bottom );
  587.     }
  588.     innermost = MIN ( currentGrafPort->portRect.right, screenRect.right );
  589.     if ( myRect.right > innermost )
  590.     {
  591.         OffsetRect ( &myRect, innermost - myRect.right, 0 );
  592.     }
  593.     innermost = MAX ( currentGrafPort->portRect.top, screenRect.top );
  594.     if ( myRect.top < innermost )
  595.     {
  596.         OffsetRect ( &myRect, 0, innermost - myRect.top );
  597.     }
  598.     innermost = MAX ( currentGrafPort->portRect.left, screenRect.left );
  599.     if ( myRect.left < innermost )
  600.     {
  601.         OffsetRect ( &myRect, innermost - myRect.left, 0 );
  602.     }
  603.             
  604.     //    Set the clip rect to myRect, including one pixel for the frame.
  605.     
  606.     ::InsetRect ( &myRect, -1, -1 );
  607.     oldClipRgn = ::NewRgn ();
  608.     ::GetClip ( oldClipRgn );
  609.     ::ClipRect ( &myRect );
  610.     
  611.     ::FrameRect ( &myRect );
  612.     
  613.     //    Draw inside the frame.
  614.     
  615.     ::InsetRect ( &myRect, 1, 1 );
  616.     ::ClipRect ( &myRect );
  617.     
  618.     if ( mPictIsLoaded )
  619.     {
  620.         //    Grab the pict handle from the pict info.
  621.         
  622.         CurrentPict    = *(*mPict);
  623.         if ( CurrentPict.Pic )
  624.         {
  625.             ::EraseRect ( &myRect );
  626.             DrawPict ( currentGrafPort, &myRect, CurrentPict.Pic, &CurrentPict.PicRect );
  627.  
  628.             //    Wait for a mouse or key event to happen. Note that we eat that event.
  629.             
  630.             Boolean done = false;
  631.             while (!done)
  632.             {
  633.                 WaitNextEvent ( everyEvent, &dummyEvent, 0, nil );
  634.                 switch ( dummyEvent.what )
  635.                 {
  636.                     case mouseDown:
  637.                     case keyDown:
  638.                     case autoKey:
  639.                         done = true;
  640.                         break;
  641.                 }
  642.             }
  643.             
  644.             //    Invalidate the rect so it gets redrawn.
  645.             
  646.             ::InsetRect ( &myRect, -1, -1 );    //    To get the frame, too.
  647.             ::InvalRect ( &myRect);
  648.         }
  649.         else
  650.         {
  651.             ErrorMessage = "\pNot Enough Ram to store picture.";
  652.         }
  653.     }
  654.     else
  655.     {
  656.         ErrorMessage = "\pPicture not loaded yet.";
  657.     }
  658.     
  659.     if ( ErrorMessage )
  660.     {
  661.         mDrawPort = (GrafPtr)(Context.Port);
  662.         mPendingDraw = true;
  663.         
  664.         //    Draw the error message.
  665.  
  666.         ::TextFace ( bold );
  667.         ::TextFont ( 0 );
  668.         ::TextSize ( 12 );
  669.         ::MoveTo ( 
  670.             (Context.Location.right + 
  671.                 Context.Location.left - 
  672.                 StringWidth(ErrorMessage))/2,
  673.             (Context.Location.bottom + Context.Location.top + 12)/2 );
  674.         ::DrawString ( ErrorMessage );
  675.     }
  676.     else
  677.     {
  678.         //    Stop invalidating the popup rect.
  679.  
  680.         mPendingDraw = false;
  681.     }
  682.  
  683.     //    Restore the old clipping region before we go.
  684.  
  685.     ::SetClip ( oldClipRgn );
  686.     ::DisposeRgn ( oldClipRgn );
  687.     
  688.     return ResultFromScode ( S_OK );
  689. }
  690.  
  691. ///////////////////////////////////////////////////////////////////////////////
  692. //
  693. //  CPopupWindowControl::IPersistPropertyBag::Load
  694. //
  695.  
  696. STDMETHODIMP
  697. CPopupWindowControl::Load ( IPropertyBag* PropBag, IErrorLog* ErrorLog )
  698. {
  699.     LPVOID            dummy;
  700.     long             i;    //    used for an index
  701.     DWORD            length;
  702.     char            propertyNameString[64];
  703.     char            propertyString[Str255BufferLength];
  704.     HRESULT            returnValue = ResultFromScode ( S_OK );
  705.     LPMONIKER        URLMoniker = NULL;
  706.     VARIANT            v;
  707.     
  708.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  709.     
  710.     //    Try to load in each property. If we can't get it, then leave things at the default.
  711.     //    Loop through all the sourceobject[x] parameters. It's possible there's only one, 
  712.     //    with no [x].
  713.     
  714.     strcpy ( propertyNameString, cSourceObjectStr );
  715.     
  716.     try
  717.     {
  718.         if ( ::LoadPropertyString ( 
  719.             PropBag, propertyNameString, propertyString, Str255StringLength, ErrorLog ) )
  720.         {
  721.             if ( !(AddDesiredSourceName ( propertyString )) )
  722.                 throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  723.         }
  724.         else
  725.         {
  726.             for ( i = 0; i <= cMaxNumSourceControls; i++ )
  727.             {        
  728.                 sprintf ( propertyNameString, "%s[%ld]", cSourceObjectStr, i );
  729.                 if ( ::LoadPropertyString ( 
  730.                     PropBag, propertyNameString, propertyString, Str255StringLength, ErrorLog ) )
  731.                 {
  732.                     if ( !(AddDesiredSourceName ( propertyString )) )
  733.                         throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  734.                 }
  735.             }
  736.         }
  737.  
  738.         v.vt = VT_BSTR;
  739.         v.bstrVal = NULL;
  740.  
  741.         //    Try to load in the property. If we can't get it, then leave things at their default.
  742.         
  743.         PropBag->Read ( "data", &v, ErrorLog );
  744.         
  745.         if ( v.bstrVal )
  746.         {
  747.             length = *((LPDWORD)(v.bstrVal)) ;
  748.             strcpy ( (Char8*)mDataURL, v.bstrVal + sizeof(Uint32) );
  749.             CoTaskMemFree ( v.bstrVal );
  750.         }
  751.         
  752.         mUnkOuterP->QueryInterface ( IID_IBindHost, (LPVOID *) &mBindSiteP );
  753.         
  754.         if ( mBindSiteP )
  755.         {
  756.             mBindSiteP->CreateMoniker ( (LPOLESTR)mDataURL, NULL, &URLMoniker, 0 );
  757.             if( URLMoniker )
  758.             {
  759.                 mBindSiteP->MonikerBindToStorage ( URLMoniker, NULL, this, IID_IStream, &dummy );
  760.                 URLMoniker->Release ();
  761.             }
  762.         }
  763.     }
  764.     
  765.     catch ( CPopupWindowError &popupWindowError )
  766.     {
  767.         //    Any failure here is fatal, because we couldn't load the control parameters.
  768.         
  769.         mFatalError = true;        
  770.         popupWindowError.HandleError ();
  771.         
  772.         returnValue = ResultFromScode ( E_FAIL );
  773.     }
  774.  
  775.     return returnValue;
  776. }
  777.  
  778. ///////////////////////////////////////////////////////////////////////////////
  779. //
  780. // CPopupWindowControl::IUnknown::QueryInterface
  781. //
  782. //  Returns a pointer to the specified interface on a component to which a
  783. //  client currently holds an interface pointer.
  784. //
  785.  
  786. STDMETHODIMP
  787. CPopupWindowControl::QueryInterface(REFIID RefID, void** Obj)
  788. {
  789.     HRESULT                returnValue = ResultFromScode ( S_OK );
  790.     
  791.     if ( mFatalError ) return ResultFromScode ( E_ABORT );
  792.     
  793.     if ( RefID == IID_IDoMenuEvents ) // an incoming interface
  794.     {
  795.         *Obj = (void*)((IDoMenuEvents*)this);
  796.         AddRef ();
  797.     }
  798.     else
  799.     {
  800.         returnValue = CBaseControl::QueryInterface ( RefID, Obj );
  801.     }
  802.     
  803.     return returnValue;
  804. }
  805.  
  806. ///////////////////////////////////////////////////////////////////////////////
  807. //
  808. //    CPopupWindowControl::SetName
  809. //
  810. //    SetName returns true if it successfully sets the name, false otherwise. It 
  811. //    can fail to allocate memory for the name.
  812.  
  813. Boolean CPopupWindowControl::SetName ( const char * theName )
  814. {
  815.     strcpy ( (char*)(&mID[1]), theName );
  816.     mID[0] = strlen(theName);
  817.     
  818.     return true;
  819.        
  820. }
  821.  
  822. ///////////////////////////////////////////////////////////////////////////////
  823. //
  824. // CPopupWindowControl::SetSourceName
  825. //
  826. Boolean CPopupWindowControl::SetSourceName ( const char * theName )
  827.     Boolean                returnValue = true;
  828.     
  829.     try
  830.     {
  831.         if ( mSourceName == NULL )
  832.         {
  833.                mSourceName = (char *) new char[Str255BufferLength];
  834.             if ( mSourceName == NULL ) throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  835.            }
  836.         
  837.            strcpy ( mSourceName, theName );
  838.     }
  839.        
  840.     catch ( CPopupWindowError &popupWindowError )
  841.     {
  842.         //    Any failure here is fatal, because we won't be able to connect to another 
  843.         //    control by name.
  844.         
  845.         mFatalError = true;        
  846.         popupWindowError.HandleError ();
  847.         
  848.         returnValue = false;
  849.     }
  850.        
  851.        return returnValue;
  852. }
  853.  
  854. ///////////////////////////////////////////////////////////////////////////////
  855. //
  856. //    CPopupWindowControl::AddDesiredSourceName
  857. //
  858. //    AddDesiredSourceName returns true if it successfully added the name to the 
  859. //    array of source names, false otherwise.
  860. //
  861. Boolean CPopupWindowControl::AddDesiredSourceName ( const char * theName )
  862. {
  863.     long            currentIndex;
  864.     long            nextIndex;
  865.     Boolean            returnValue = true;
  866.     
  867.     ASSERT ( theName != NULL, "Unexpected NULL source name in AddDesiredSourceName" );
  868.     
  869.     currentIndex = ( mNumDesiredSources < 0 ) ? 0 : mNumDesiredSources;
  870.     nextIndex = currentIndex + 1;
  871.     
  872.     if ( nextIndex < cMaxNumSourceControls )
  873.     {
  874.         try
  875.         {
  876.             mDesiredSourceNames[currentIndex] = new char[strlen(theName)+1];
  877.             if ( mDesiredSourceNames[currentIndex] == NULL )
  878.                 throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  879.             
  880.             strcpy(mDesiredSourceNames[currentIndex], theName);
  881.             mNumDesiredSources = nextIndex;
  882.         }
  883.         
  884.         catch ( CPopupWindowError &popupWindowError )
  885.         {
  886.             //    Any failure here is fatal, because we don't another control to connect to.
  887.             
  888.             mFatalError = true;        
  889.             popupWindowError.HandleError ();
  890.             
  891.             returnValue = false;
  892.         }
  893.     }
  894.     
  895.     return returnValue;
  896. }
  897.  
  898. ///////////////////////////////////////////////////////////////////////////////
  899. //
  900. //    CPopupWindowControl::AddSource
  901. //    
  902. //    AddSource sets the alreadyFoundIt parameter to true if it already exists in 
  903. //    the list.
  904. //    
  905. //    AddSource returns false if it has an allocation error, true otherwise.
  906.  
  907. Boolean CPopupWindowControl::AddSource ( const char * sourceName, Boolean * alreadyFoundIt )
  908. {
  909.     long                i;    //    used as an index
  910.     Boolean                returnValue = true;
  911.     
  912.     //    Only add the source to our list of found sources if we haven't already done so.
  913.     
  914.     *alreadyFoundIt = false;
  915.     
  916.     for ( i = 0; i < mNumFoundSources; i++ )
  917.     {
  918.         if ( STRINGS_ARE_EQUAL ( mFoundSourceNames[i], sourceName ) )
  919.         {
  920.             //    It's already in our list.
  921.             
  922.             *alreadyFoundIt = true;
  923.             break;
  924.         }
  925.     }
  926.     
  927.     if ( !(*alreadyFoundIt) )
  928.     {
  929.         try
  930.         {
  931.             mFoundSourceNames[mNumFoundSources] = new char[strlen(sourceName)+1];
  932.             if ( mFoundSourceNames[mNumFoundSources]  == NULL)
  933.                 throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  934.             
  935.             strcpy(mFoundSourceNames[mNumFoundSources], sourceName);
  936.             mNumFoundSources++;
  937.         }
  938.         
  939.         catch ( CPopupWindowError &popupWindowError )
  940.         {
  941.             //    Any failure here is fatal, because we don't have a source control.
  942.             
  943.             mFatalError = true;        
  944.             popupWindowError.HandleError ();
  945.             
  946.             returnValue = false;
  947.         }
  948.     }
  949.     
  950.     return returnValue;
  951. }
  952.  
  953. ///////////////////////////////////////////////////////////////////////////////
  954. //
  955. // CPopupWindowControl::IsSource
  956. //
  957. Boolean CPopupWindowControl::IsSource ( IUnknown * unk )
  958. {
  959.     Boolean            isSource = false;
  960.     char            sourceName[256];
  961.     
  962.     if ( mNumDesiredSources == -1 )    //    We're not being specific.
  963.     {
  964.         isSource = true;
  965.     }
  966.     else
  967.     {
  968.         GetObjectName ( unk, sourceName );
  969.         
  970.         if ( sourceName )
  971.         {
  972.             isSource = IsSource ( sourceName );
  973.         }
  974.     }
  975.     
  976.     return isSource;
  977. }
  978.  
  979. Boolean CPopupWindowControl::IsSource ( const char * sourceName )
  980. {
  981.     long                i;    //    used as an index
  982.     Boolean                isSource = false;
  983.     
  984.     if ( mNumDesiredSources == -1 )    //    We're not being specific.
  985.     {
  986.         isSource = true;
  987.     }
  988.     else
  989.     {        
  990.         for ( i = 0; i < mNumFoundSources; i++ )
  991.         {
  992.             ASSERT(mFoundSourceNames[i] != NULL, "Unexpected NULL source name!");
  993.             
  994.             if ( STRINGS_ARE_EQUAL ( mFoundSourceNames[i], sourceName ) )
  995.             {
  996.                 //    It's one of our targets.
  997.                 
  998.                 isSource = true;
  999.                 break;    //    There's no need to loop any further once we've matched.
  1000.             }
  1001.         }
  1002.     }
  1003.     
  1004.     return isSource;
  1005. }
  1006.  
  1007. ///////////////////////////////////////////////////////////////////////////////
  1008. //
  1009. //  CPopupWindowControl::DrawPict
  1010. //
  1011.  
  1012. void CPopupWindowControl::DrawPict (
  1013.     GrafPtr        pGrafDraw, 
  1014.     Rect*        lprect, 
  1015.     PicHandle    Pic, 
  1016.     Rect*        PictRect)
  1017. {
  1018.  
  1019. #pragma unused (pGrafDraw)
  1020.  
  1021.     Rect theRect;
  1022.  
  1023.     theRect.top = lprect->top;
  1024.     theRect.left = lprect->left;
  1025.     theRect.bottom = lprect->top + PictRect->bottom;
  1026.     theRect.right = lprect->left + PictRect->right;
  1027.     
  1028.     ::DrawPicture ( Pic, &theRect );
  1029.     
  1030.     return;
  1031. }
  1032.  
  1033. ///////////////////////////////////////////////////////////////////////////////
  1034. //
  1035. //  CPopupWindowControl::AllocatePictBuffer
  1036. //
  1037. //    AllocatePictBuffer allocates a buffer in memory before the picture is read 
  1038. //    in, and each time new data is available AllocatePictBuffer bumps up the 
  1039. //    buffer size.
  1040. //
  1041. //    AllocatePictBuffer returns true if the allocation was successful, false 
  1042. //    otherwise.
  1043. //
  1044. Boolean CPopupWindowControl::AllocatePictBuffer ( void )
  1045. {
  1046.     unsigned long    newSize;
  1047.     Boolean            returnValue = true;
  1048.     Handle            TargetPicHandle = NULL;
  1049.     
  1050.     try
  1051.     {
  1052.         if ( ( mPict == NULL) && ( mDataSize > 0 ) )
  1053.         {
  1054.             //    Allocate memory for a new picture record and its buffer.
  1055.             
  1056.             mPict = (pictInfo**)(::NewHandle ( sizeof(pictInfo) ));
  1057.             if ( mPict == NULL ) throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  1058.             
  1059.             ::HLock ( (Handle)mPict );
  1060.             (**mPict).Pic = (PicHandle)(::NewHandle ( mDataSize ));
  1061.             ::HUnlock( (Handle)mPict );
  1062.             if ( (**mPict).Pic == NULL ) throw CPopupWindowError ( DATA_ALLOCATION_ERROR );
  1063.             
  1064.             //    Let everyone know we're just starting.
  1065.             
  1066.             mPictIsLoaded = false;
  1067.             mPictBytesSoFar = 0;
  1068.         }
  1069.         else if ( mDataSize > 0 )    //    ( mPict != NULL ); don't bother if the amount of data 
  1070.         {                        //    available is zero.
  1071.             if ( (**mPict).Pic != NULL )
  1072.             {
  1073.                 //    Bump up the memory for the picture buffer so we have space to read into.
  1074.                 
  1075.                 TargetPicHandle = (Handle)((*mPict)->Pic);
  1076.                 ::SetHandleSize ( TargetPicHandle, mPictBytesSoFar + mDataSize );
  1077.                 newSize = ::GetHandleSize ( TargetPicHandle );
  1078.                 if ( newSize != mPictBytesSoFar + mDataSize ) 
  1079.                     throw CPopupWindowError ( DATA_REALLOCATION_ERROR );
  1080.             }
  1081.         }
  1082.     }
  1083.     
  1084.     catch ( CPopupWindowError &popupWindowError )
  1085.     {
  1086.         //    Any failure here is fatal; because if we don't have enough memory 
  1087.         //    for an image buffer, we can't render the image. Duh.
  1088.         
  1089.         mFatalError = true;        
  1090.         popupWindowError.HandleError ();
  1091.         
  1092.         returnValue = false;
  1093.     }
  1094.     
  1095.     return returnValue;
  1096. }
  1097.  
  1098. ///////////////////////////////////////////////////////////////////////////////
  1099. //
  1100. //  CPopupWindowControl::ReadPictHeader
  1101. //
  1102. //    ReadPictHeader reads the pict header from the input stream into the pict 
  1103. //    buffer. 
  1104. //    
  1105. //    ReadPictHeader is called by the OnDataAvailable callback, so it is possible 
  1106. //    all of the data isn't available when ReadPictHeader is called. If not, it 
  1107. //    will continue reading when it is called repeatedly until all of the pict 
  1108. //    header has been read.
  1109. //    
  1110. //    ReadPictHeader returns true if successful, false otherwise.
  1111. //
  1112. Boolean CPopupWindowControl::ReadPictHeader ( STGMEDIUM* StorageMedium )
  1113. {
  1114.     unsigned long    actualBytesRead = 0;
  1115.     unsigned long    BytesToRead = 0L;
  1116.     unsigned long    headerBytesRemaining = 0;
  1117.     HRESULT            resultCode = ResultFromScode ( S_OK );
  1118.     Boolean            returnValue = true;
  1119.     char*            Target = NULL;
  1120.     Handle            TargetPicHandle = NULL;
  1121.     
  1122.     TargetPicHandle = (Handle)((*mPict)->Pic);
  1123.     ::HLock ( TargetPicHandle );
  1124.     Target = *TargetPicHandle;
  1125.     
  1126.     headerBytesRemaining =  ( cPictHeaderSize - mPictBytesSoFar > 0 ) ? 
  1127.         cPictHeaderSize - mPictBytesSoFar : 0;
  1128.     BytesToRead = ( headerBytesRemaining < mDataSize ) ? headerBytesRemaining : mDataSize;
  1129.     
  1130.     try
  1131.     {
  1132.         resultCode = StorageMedium->pstm->Read ( Target, 
  1133.                                                  BytesToRead, 
  1134.                                                  &actualBytesRead );
  1135.         ::HUnlock ( TargetPicHandle );
  1136.         
  1137.         if ( FAILED ( resultCode ) ) throw CPopupWindowError ( IMAGE_READ_ERROR );
  1138.  
  1139.         mPictBytesRemaining -= actualBytesRead;
  1140.         mPictBytesSoFar += actualBytesRead;
  1141.     }
  1142.     
  1143.     catch ( CPopupWindowError &popupWindowError )
  1144.     {
  1145.         //    Are we guaranteed that a failure here is fatal?
  1146.         
  1147.         mFatalError = true;
  1148.         popupWindowError.HandleError ();
  1149.         
  1150.         returnValue = false;
  1151.     }
  1152.  
  1153.     return returnValue;
  1154. }
  1155.  
  1156. ///////////////////////////////////////////////////////////////////////////////
  1157. //
  1158. //  CPopupWindowControl::ReadPictData
  1159. //
  1160. //    ReadPictData reads the pict data from the input stream into the pict buffer. 
  1161. //    
  1162. //    ReadPictData is called by the OnDataAvailable callback, so it is possible 
  1163. //    all of the data isn't available when ReadPictData is called. If not, it 
  1164. //    will continue reading when it is called repeatedly until all of the pict 
  1165. //    data has been read.
  1166. //
  1167. //    ReadPictData returns true if it is successful, false otherwise.
  1168. //
  1169. Boolean CPopupWindowControl::ReadPictData ( STGMEDIUM* StorageMedium )
  1170. {
  1171.     unsigned long    actualBytesRead = 0;
  1172.     unsigned long    BytesToRead = 0L;
  1173.     HRESULT            resultCode = S_OK;
  1174.     Boolean            returnValue = true;
  1175.     char*            Target = NULL;
  1176.     Handle            TargetPicHandle = NULL;
  1177.     
  1178.     //    Read the available data into the picture buffer.
  1179.     
  1180.     TargetPicHandle = (Handle)((*mPict)->Pic);
  1181.     ::HLock ( TargetPicHandle );
  1182.     
  1183.     BytesToRead = mPictBytesRemaining;
  1184.     
  1185.     //    Advance the target through the PicHandle as we go. Note that we overwrite 
  1186.     //    the pict header data we previously read into the target buffer.
  1187.     
  1188.     Target = *TargetPicHandle + mPictBytesSoFar - cPictHeaderSize;
  1189.     
  1190.     try
  1191.     {
  1192.         resultCode = StorageMedium->pstm->Read ( Target, 
  1193.                                                  BytesToRead, 
  1194.                                                  &actualBytesRead );
  1195.         ::HUnlock ( TargetPicHandle );
  1196.         if ( FAILED ( resultCode ) ) throw CPopupWindowError ( IMAGE_READ_ERROR );
  1197.         
  1198.         mPictBytesRemaining -= actualBytesRead;
  1199.         mPictBytesSoFar += actualBytesRead;
  1200.     }
  1201.     
  1202.     catch ( CPopupWindowError &popupWindowError )
  1203.     {
  1204.         //    Are we guaranteed that a failure here is fatal?
  1205.         
  1206.         mFatalError = true;
  1207.         popupWindowError.HandleError ();
  1208.         
  1209.         returnValue = false;
  1210.     }
  1211.  
  1212.     return returnValue;
  1213. }
  1214.